package com.hzw.saas.web.shell.controller;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.extra.servlet.ServletUtil;
import com.hzw.saas.api.hpc.IHpcAccountRecordService;
import com.hzw.saas.api.hpc.IHpcAccountService;
import com.hzw.saas.api.hpc.IHpcAccountUserService;
import com.hzw.saas.api.hpc.bo.HpcAccountBo;
import com.hzw.saas.api.hpc.bo.HpcAccountRecordBo;
import com.hzw.saas.api.hpc.bo.HpcAccountUserBo;
import com.hzw.saas.api.storage.bo.InxChunkBO;
import com.hzw.saas.common.config.annotation.SysLog;
import com.hzw.saas.common.config.exception.DefaultSaasException;
import com.hzw.saas.common.config.util.AssertUtil;
import com.hzw.saas.common.security.pojo.dto.SaasUser;
import com.hzw.saas.common.security.utils.SecurityUtils;
import com.hzw.saas.common.util.HttpContextUtils;
import com.hzw.saas.common.util.MapperUtil;
import com.hzw.saas.web.shell.controller.param.*;
import com.hzw.saas.web.shell.service.ISftpFileService;
import com.hzw.saas.web.shell.service.bo.SftpFileBO;
import com.hzw.saas.web.shell.service.bo.SshConnBO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.util.List;
import java.util.Objects;

/**
 * <p>
 *
 * </p>
 *
 * @author sonam
 * @since 2021/5/29 11:35
 */
@RestController
@RequiredArgsConstructor
@Slf4j
@Api(tags = "HPC文件管理")
public class SftpController {

    // private final ApplicationEventPublisher publisher;

    private final ISftpFileService sftpFileService;
    private final IHpcAccountUserService hpcAccountUserService;
    private final IHpcAccountRecordService hpcAccountRecordService;
    private final IHpcAccountService hpcAccountService;

    @GetMapping("/hpc/{hpcAccountUserId}/resource/exists")
    @SysLog(operation = "检查文件/目录是否存在")
    @ApiOperation(value = "检查文件/目录是否存在", notes = "检查文件/目录是否存在")
    @ApiImplicitParams({
        @ApiImplicitParam(name = "hpcAccountUserId", value = "hpcAccountUserId", required = true, dataType = "String", paramType = "path"),
        @ApiImplicitParam(name = "uuid", value = "会话uuid，请保证唯一性", required = true, dataType = "String", paramType = "query"),
        @ApiImplicitParam(name = "path", value = "资源路径", required = true, dataType = "String", paramType = "query")
    })
    public ResponseEntity<Boolean> existsPath(@PathVariable String hpcAccountUserId, String path, String uuid) {
        SaasUser user = SecurityUtils.getUser();
        SshConnBO sshConn = this.getSshConn(user.getUserId(), hpcAccountUserId, uuid);
        boolean exist = sftpFileService.exist(sshConn, path);
        return ResponseEntity.ok(exist);
    }

    @GetMapping("/hpc/{hpcAccountUserId}/home")
    @SysLog(operation = "获取hpc用户目录")
    @ApiOperation(value = "获取hpc用户目录", notes = "获取hpc用户目录")
    @ApiImplicitParams({
        @ApiImplicitParam(name = "hpcAccountUserId", value = "hpcAccountUserId", required = true, dataType = "String", paramType = "path"),
        @ApiImplicitParam(name = "uuid", value = "会话uuid，请保证唯一性", required = true, dataType = "String", paramType = "query")
    })
    public ResponseEntity<String> home(@PathVariable String hpcAccountUserId, String uuid) {
        SaasUser user = SecurityUtils.getUser();
        SshConnBO sshConn = this.getSshConn(user.getUserId(), hpcAccountUserId, uuid);
        String home = sftpFileService.getHome(sshConn);
        return ResponseEntity.ok(home);
    }

    @GetMapping("/hpc/{hpcAccountUserId}/resources")
    @SysLog(operation = "查看目标目录的子目录和子文件")
    @ApiOperation(value = "查看目标目录的子目录和子文件", notes = "检查文件/目录是否存在")
    @ApiImplicitParams({
        @ApiImplicitParam(name = "hpcAccountUserId", value = "hpcAccountUserId", required = true, dataType = "String", paramType = "path"),
        @ApiImplicitParam(name = "uuid", value = "会话uuid，请保证唯一性", required = true, dataType = "String", paramType = "query"),
        @ApiImplicitParam(name = "supPath", value = "父目录路径", required = true, dataType = "String", paramType = "query")
    })
    public ResponseEntity<List<SftpFileBO>> listFiles(@PathVariable String hpcAccountUserId, String supPath, String uuid) {
        SaasUser user = SecurityUtils.getUser();
        SshConnBO sshConn = this.getSshConn(user.getUserId(), hpcAccountUserId, uuid);
        List<SftpFileBO> list = sftpFileService.list(sshConn, supPath);
        return ResponseEntity.ok(list);
    }

    @PostMapping("/hpc/folder")
    @SysLog(operation = "创建目录", mode = 3)
    @ApiOperation(value = "创建目录", notes = "创建目录，调用此接口前请先调用检查文件/目录是否存在接口，检查目标路径是否存在同名目录")
    public ResponseEntity<Boolean> mkdir(@Validated @RequestBody MkdirParam param) {
        SaasUser user = SecurityUtils.getUser();
        SshConnBO sshConn = this.getSshConn(user.getUserId(), param.getHpcAccountUserId(), param.getUuid());
        sftpFileService.mkdir(sshConn, param.getDirPath());
        return ResponseEntity.ok(true);
    }

    @PostMapping("/hpc/resource/rename")
    @SysLog(operation = "重命名目录/文件", mode = 3)
    @ApiOperation(value = "重命名目录/文件", notes = "重命名目录/文件，此接口会覆盖同名文件/目录，调用此接口前请先调用检查文件/目录是否存在接口，检查目标路径是否存在同名文件/目录")
    public ResponseEntity<Boolean> rename(@Validated @RequestBody RenameFileParam param) {
        SaasUser user = SecurityUtils.getUser();
        SshConnBO sshConn = this.getSshConn(user.getUserId(), param.getHpcAccountUserId(), param.getUuid());
        sftpFileService.rename(sshConn, param.getSrcPath(), param.getNewName());
        return ResponseEntity.ok(true);
    }

    @PostMapping("/hpc/resource/move")
    @SysLog(operation = "移动目录/文件", mode = 3)
    @ApiOperation(value = "移动目录/文件", notes = "移动目录/文件，此接口会覆盖同名文件/目录，调用此接口前请先调用检查文件/目录是否存在接口，检查目标路径是否存在同名文件/目录")
    public ResponseEntity<Boolean> mv(@Validated @RequestBody MvFileParam param) {
        SaasUser user = SecurityUtils.getUser();
        SshConnBO sshConn = this.getSshConn(user.getUserId(), param.getHpcAccountUserId(), param.getUuid());
        sftpFileService.mv(sshConn, param.getSrcPaths(), param.getTarDirPath());
        return ResponseEntity.ok(true);
    }

    @PostMapping("/hpc/resource/copy")
    @SysLog(operation = "复制目录/文件", mode = 3)
    @ApiOperation(value = "复制目录/文件", notes = "复制目录/文件，此接口会覆盖同名文件/目录，调用此接口前请先调用[检查文件/目录是否存在]接口，检查目标路径是否存在同名文件/目录")
    public ResponseEntity<Boolean> cp(@Validated @RequestBody MvFileParam param) {
        SaasUser user = SecurityUtils.getUser();
        SshConnBO sshConn = this.getSshConn(user.getUserId(), param.getHpcAccountUserId(), param.getUuid());
        sftpFileService.cp(sshConn, param.getSrcPaths(), param.getTarDirPath());
        return ResponseEntity.ok(true);
    }

    @DeleteMapping("/hpc/resource/")
    @SysLog(operation = "删除目录/文件", mode = 3)
    @ApiOperation(value = "删除目录/文件", notes = "删除目录/文件，调用此接口前请先调用[查看目标目录的子目录和子文件]接口,查看目录是否含有子文件并进行提醒")
    public ResponseEntity<Boolean> del(@Validated @RequestBody DelFileParam param) {
        SaasUser user = SecurityUtils.getUser();
        if (CollectionUtil.isNotEmpty(param.getPathList())) {
            SshConnBO sshConn = this.getSshConn(user.getUserId(), param.getHpcAccountUserId(), param.getUuid());
            for (String path : param.getPathList()) {
                sftpFileService.rm(sshConn, path);
            }
        }
        return ResponseEntity.ok(true);
    }

    @PostMapping(value = "/hpc/file/chunk", consumes = {MediaType.MULTIPART_FORM_DATA_VALUE})
    @ApiOperation(value = "文件分片上传", notes = "文件分片上传，请按照文件分片顺序依次调用此接口。最后一片分片上传完成返回200时，即文件上传完成")
    public ResponseEntity<Void> uploadChunk(@Validated HpcInxChunkParam chunkData, HttpServletRequest request) {
        // 判断请求类型
        boolean multipart = ServletUtil.isMultipart(request);
        AssertUtil.assertThrow("上传失败，请求参数异常，请使用multipart/form-data类型上传", !multipart);
        SaasUser user = SecurityUtils.getUser();
        SshConnBO sshConn = this.getSshConn(user.getUserId(), chunkData.getHpcAccountUserId(), chunkData.getUuid());
        InxChunkBO inxChunkBO = MapperUtil.nf().map(chunkData, InxChunkBO.class);
        try {
            sftpFileService.transferChunkOnSftp(sshConn, chunkData.getSupPath(), inxChunkBO, chunkData.getChunkFile().getInputStream());
            return ResponseEntity.ok().build();
        } catch (IOException e) {
            throw DefaultSaasException.build("上传文件异常", e);
        }
    }

    @GetMapping("/hpc/{hpcAccountUserId}/file")
    @SysLog(operation = "下载hpc文件", mode = 3)
    @ApiOperation(value = "下载hpc文件", notes = "下载hpc文件")
    @ApiImplicitParams({
        @ApiImplicitParam(name = "access_token", value = "用户token（或在请求头设置Authorization）", dataType = "String", paramType = "query", required = false),
        @ApiImplicitParam(name = "hpcAccountUserId", value = "hpcAccountUserId", dataType = "String", paramType = "path", required = true),
        @ApiImplicitParam(name = "uuid", value = "uuid", dataType = "String", paramType = "query", required = true),
        @ApiImplicitParam(name = "path", value = "文件路径", dataType = "String", paramType = "query", required = true)
    })
    public void downloadFile(@PathVariable String hpcAccountUserId, String uuid, String path, HttpServletRequest request, HttpServletResponse response) {
        SaasUser user = SecurityUtils.getUser();
        SshConnBO sshConn = this.getSshConn(user.getUserId(), hpcAccountUserId, uuid);
        SftpFileBO fileInfo = sftpFileService.getFileInfo(sshConn, path);
        AssertUtil.assertThrow("文件不存在", Objects.isNull(fileInfo));
        try (OutputStream fos = response.getOutputStream()) {
            HttpContextUtils.setFileDownloadHeader(request, response, fileInfo.getName(), fileInfo.getLength());
            sftpFileService.download(sshConn, path, fos);
            response.flushBuffer();
        } catch (IOException e) {
            throw DefaultSaasException.build("下载文件异常");
        }
    }

    @DeleteMapping("/hpc/{hpcAccountUserId}/session/{uuid}")
    @SysLog(operation = "释放用户与HPC的连接", mode = 3)
    @ApiOperation(value = "释放用户与HPC的连接", notes = "释放用户与HPC的连接")
    @ApiImplicitParams({
        @ApiImplicitParam(name = "hpcAccountUserId", value = "hpcAccountUserId", required = true, dataType = "String", paramType = "path")
    })
    public void closeUserSession(@PathVariable String hpcAccountUserId, @PathVariable String uuid) {
        SaasUser user = SecurityUtils.getUser();
        SshConnBO sshConn = this.getSshConn(user.getUserId(), hpcAccountUserId, uuid);
        sftpFileService.close(sshConn);
    }

    private SshConnBO getSshConn(String userId, String hpcAccountUserId, String uuid) {
        HpcAccountUserBo hpcAccountUserBo = hpcAccountUserService.get(hpcAccountUserId);
        AssertUtil.assertThrow("hpcAccountUser未找到", Objects.isNull(hpcAccountUserBo));
        AssertUtil.assertThrow("用户无此HPC账户权限", !Objects.equals(userId, hpcAccountUserBo.getUserId()));
        HpcAccountRecordBo hpcAccountRecordBo = hpcAccountRecordService.get(hpcAccountUserBo.getHpcAccountRecordId());
        AssertUtil.assertThrow("hpcAccountRecord未找到", Objects.isNull(hpcAccountRecordBo));
        HpcAccountBo hpcAccount = hpcAccountService.getById(hpcAccountRecordBo.getHpcAccountId());
        AssertUtil.assertThrow("hpcAccount未找到", Objects.isNull(hpcAccount));
        SshConnBO sshConnBO = new SshConnBO();
        sshConnBO.setSid(uuid);
        sshConnBO.setHost(hpcAccount.getHost());
        sshConnBO.setPort(hpcAccount.getPort());
        sshConnBO.setUsername(hpcAccount.getHpcAccount());
        sshConnBO.setPassword(hpcAccount.getHpcPassword());
        return sshConnBO;
    }

    // @SneakyThrows
    // @ApiOperation("大文件分片上传")
    // @PostMapping(value = "chunkUpload", consumes = {"multipart/form-data"})
    // public ResponseEntity<Object> fileChunkUpload(MultipartFile file,
    //                                               Integer start,
    //                                               Integer end,
    //                                               Integer totalPieces,
    //                                               Integer index,
    //                                               HttpServletResponse response,
    //                                               HttpServletRequest request) {
    //     Session session = JschUtil.openSession("ssh.paracloud.com", 22, "sc80532@BSCC-T", "S16ykvoR4pbu78ctiUB59NMTlr32A0Yd");
    //     Sftp sftp = JschUtil.createSftp(session);
    //     ChannelSftp client = sftp.getClient();
    //     client.put(file.getInputStream(), "/public1/home/sc80532/test/" + file.getOriginalFilename(), new SftpProgressMonitor() {
    //         @Override
    //         public void init(int op, String src, String dest, long max) {
    //
    //         }
    //
    //         @Override
    //         public boolean count(long count) {
    //             publisher.publishEvent(new ShellEvent(SecurityUtils.getUser().getUsername(), "/webshell/upload/" + SecurityUtils.getUser().getUsername(), count + ""));
    //             return true;
    //         }
    //
    //         @Override
    //         public void end() {
    //
    //         }
    //     }, ChannelSftp.APPEND);
    //
    //     log.info(StrFormatter.format("{} -> {}, 当前进度{}，一共{}，上传大小{}。", file.getName(), file.getSize(), index, totalPieces, end));
    //
    //     return ResponseEntity.ok(file.getSize());
    // }


}
